package com.jlect.swebing.renderers.gwt.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.impl.PopupImpl;
import com.jlect.swebing.renderers.client.PopupListener;
import com.jlect.swebing.renderers.client.PopupListenerCollection;

/**
 * This panel in contrary to the {@link LightPopup} doesn't do any actions by itself (like hiding and etc) but simply
 * show up under the other components
 * <p/>
 * Note: this class implementation is get basicly from {@link com.google.gwt.user.client.ui.PopupPanel}.
 * The only changes are made - listeners adaptation and event preview was removed. Also it doesn't try to resize inner
 * children using it's size.
 *
 * @author Sergey Kozmin
 * @since 27.11.2007 10:14:36
 */
public class LightPopup extends SimplePanel {
    private static final PopupImpl impl = (PopupImpl) GWT.create(PopupImpl.class);

    private boolean showing;

    // the left style attribute in pixels
    private int leftPosition = -1;

    // The top style attribute in pixels
    private int topPosition = -1;

    private PopupListenerCollection popupListeners;

    /**
     * Creates an empty popup panel. A child widget must be added to it before it
     * is shown.
     */
    public LightPopup() {
        super(impl.createElement());

        // Default position of popup should be in the upper-left corner of the
        // window. By setting a default position, the popup will not appear in
        // an undefined location if it is shown before its position is set.
        setPopupPosition(0, 0);
    }

    public void addPopupListener(PopupListener listener) {
        if (popupListeners == null) {
            popupListeners = new PopupListenerCollection();
        }
        popupListeners.add(listener);
    }

    public void removeAllPopupListeners() {
        if (popupListeners != null) {
            popupListeners.clear();
        }
    }

    /**
     * Centers the popup in the browser window and shows it. If the popup was
     * already showing, then the popup is centered.
     */
    public void center() {
        boolean initiallyShowing = showing;

        if (!initiallyShowing) {
            setVisible(false);
            show();
        }

        int left = (Window.getClientWidth() - getOffsetWidth()) / 2;
        int top = (Window.getClientHeight() - getOffsetHeight()) / 2;
        setPopupPosition(Window.getScrollLeft() + left, Window.getScrollTop() + top);

        if (!initiallyShowing) {
            setVisible(true);
        }
    }

    /**
     * Gets the panel's offset height in pixels. Calls to {@link #setHeight(String)}
     * before the panel's child widget is set will not influence the offset height.
     *
     * @return the object's offset height
     */
    public int getOffsetHeight() {
        return super.getOffsetHeight();
    }

    /**
     * Gets the panel's offset width in pixels. Calls to {@link #setWidth(String)}
     * before the panel's child widget is set will not influence the offset width.
     *
     * @return the object's offset width
     */
    public int getOffsetWidth() {
        return super.getOffsetWidth();
    }

    /**
     * Gets the popup's left position relative to the browser's client area.
     *
     * @return the popup's left position
     */
    public int getPopupLeft() {
        return DOM.getElementPropertyInt(getElement(), "offsetLeft");
    }

    /**
     * Gets the popup's top position relative to the browser's client area.
     *
     * @return the popup's top position
     */
    public int getPopupTop() {
        return DOM.getElementPropertyInt(getElement(), "offsetTop");
    }

    public String getTitle() {
        return DOM.getElementProperty(getContainerElement(), "title");
    }

    public void removePopupListener(PopupListener listener) {
        if (popupListeners != null) {
            popupListeners.remove(listener);
        }
    }

    /**
     * Sets the popup's position relative to the browser's client area. The
     * popup's position may be set before calling {@link #show()}.
     *
     * @param left the left position, in pixels
     * @param top  the top position, in pixels
     */
    public void setPopupPosition(int left, int top) {
        // Keep the popup within the browser's client area, so that they can't get
        // 'lost' and become impossible to interact with. Note that we don't attempt
        // to keep popups pegged to the bottom and right edges, as they will then
        // cause scrollbars to appear, so the user can't lose them.
        if (left < 0) {
            left = 0;
        }
        if (top < 0) {
            top = 0;
        }

        // Save the position of the popup
        leftPosition = left;
        topPosition = top;

        // Set the popup's position manually, allowing setPopupPosition() to be
        // called before show() is called (so a popup can be positioned without it
        // 'jumping' on the screen).
        Element elem = getElement();
        DOM.setStyleAttribute(elem, "left", left + "px");
        DOM.setStyleAttribute(elem, "top", top + "px");
    }

    /**
     * Sets the popup's position using a {@link com.google.gwt.user.client.ui.PopupPanel.PositionCallback}, and shows
     * the popup. The callback allows positioning to be performed based on
     * the offsetWidth and offsetHeight of the popup, which are normally
     * not available until the popup is showing. By positioning the popup
     * before it is shown, the the popup will not jump from its original
     * position to the new position.
     *
     * @param callback the callback to set the position of the popup
     * @see com.google.gwt.user.client.ui.PopupPanel.PositionCallback#setPosition(int offsetWidth, int offsetHeight)
     */
    public void setPopupPositionAndShow(PopupPanel.PositionCallback callback) {
        setVisible(false);
        show();
        callback.setPosition(getOffsetWidth(), getOffsetHeight());
        setVisible(true);
    }

    public void setTitle(String title) {
        Element containerElement = getContainerElement();
        if (title == null || title.length() == 0) {
            DOM.removeElementAttribute(containerElement, "title");
        } else {
            DOM.setElementAttribute(containerElement, "title", title);
        }
    }

    /**
     * Sets whether this object is visible.
     *
     * @param visible <code>true</code> to show the object, <code>false</code>
     *                to hide it
     */
    public void setVisible(boolean visible) {
        // We use visibility here instead of UIObject's default of display
        // Because the panel is absolutely positioned, this will not create
        // "holes" in displayed contents and it allows normal layout passes
        // to occur so the size of the PopupPanel can be reliably determined.
        DOM.setStyleAttribute(getElement(), "visibility", visible ? "visible"
                : "hidden");

        // If the PopupImpl creates an iframe shim, it's also necessary to hide it
        // as well.
        impl.setVisible(getElement(), visible);
    }

    public void show() {
        if (showing) {
            return;
        }
        showing = true;

        DOM.setStyleAttribute(getElement(), "position", "absolute");
        if (topPosition != -1) {
            setPopupPosition(leftPosition, topPosition);
        }
        RootPanel.get().add(this);

        impl.onShow(getElement());
        if (popupListeners != null) {
            popupListeners.firePopupVisibilityChanged(true);
        }
    }

    protected Element getContainerElement() {
        return impl.getContainerElement(getElement());
    }

    protected Element getStyleElement() {
        return impl.getContainerElement(getElement());
    }

    public void hide() {
        if (!showing) {
            return;
        }
        showing = false;

        RootPanel.get().remove(this);
        impl.onHide(getElement());
        if (popupListeners != null) {
            popupListeners.firePopupVisibilityChanged(false);
        }
    }

    public boolean isShowing() {
        return showing;
    }
}
